package com.weic.easycache.task;

import java.lang.ref.SoftReference;
import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicInteger;

import com.weic.easycache.Debug;
import com.weic.easycache.utils.NumberUtils;

import android.annotation.SuppressLint;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import android.os.Process;
import android.util.AndroidRuntimeException;
import android.util.Log;

public class TaskQueue {
	/**根据多线程下载的经验，这里15个线程的并发对UI流畅性的影响并不太大，而且
	 * 设置了优先级为Process.THREAD_PRIORITY_BACKGROUND，会尽量减少对UI线程的影响**/
	public static final int maxTaskThreadSize = 15;
	public static final int maxFreePoolSize = 6;

	public static TaskBuilder builder() {
		return new TaskBuilder();
	}

	public static TaskQueue obtain() {
		if(LOG) Log.i(LOG_TAG, "obtain----------executorPoolSize: " + freeExecutorPool.size());
		TaskQueue taskQueue = null;
		SoftReference<TaskQueue> ref;
		while(true) {
			ref = freeExecutorPool.poll();
			if(ref == null) break;
			taskQueue = ref.get();
			if(taskQueue != null) break;
		}
		if(taskQueue == null) {
			taskQueue = new TaskQueue();
			if(LOG) Log.w(LOG_TAG, "obtain----------重新创建~~~~~~~~~~~~~~");
		}else {
			taskQueue.mStarted = false;
			if(LOG) Log.i(LOG_TAG, "obtain----------从executorPool获取成功--------------");
		}
		return taskQueue;
	}

	private static final String LOG_TAG		= "TaskQueue";
	private static final boolean LOG		= Debug.LOG;

	private volatile int mSleepTimeMillis = 0;
	private volatile boolean mSlow = false;

	private volatile boolean mExecutorPrepared		= false;
	private volatile boolean mStarted				= false;
	private volatile boolean mIsBgTask				= false;
	private volatile int mCurrentTaskIndex			= 0;
	private volatile int mTaskSize					= 0;

	private final Queue<Task> mTasks = new LinkedList<Task>();
	private final Queue<Task> mBgTasks = new LinkedList<Task>();
	private TaskThread mTaskThread;
	private volatile TaskListener mTaskListener;

	/**将任务压入队列。注意：任务启动执行时无法再压入。**/
	public synchronized final void queueTask(Task task) {
		if(mStarted)  throw new AndroidRuntimeException("任务正在执行，不可再插入新任务");
		mTasks.offer(task);
	}

	public final void executeTasks() {
		synchronized(this) {
			if(mStarted) return;
			mStarted = true;
		}
		mTaskSize = mTasks.size();
		if(mTaskListener != null) mTaskListener.onStart();

		initExecutor();
	}

	private void executeBgTasks() {
		mTasks.clear();
		mTasks.addAll(mBgTasks);
		mBgTasks.clear();
		for(Task task : mTasks) {
			task.reset();
		}
		mIsBgTask = true;
		mCurrentTaskIndex = 0;
		mTaskSize = mTasks.size();
		initExecutor();
	}

	private void initExecutor() {
		if(mTaskThread == null) {
			mTaskThread = new TaskThread(this);
			mTaskThread.start();
		}else if(mExecutorPrepared) {
			onExecutorPrepared();
		}else {
			//等待就绪
		}
	}

	private void onExecutorPrepared() {
		mExecutorPrepared = true;
		nextTask();
	}

	private void nextTask() {
		Task task = mTasks.poll();
		if(task != null) {
			mTaskThread.nextTask(task, getSleepTime());
		}else {
			/* 发送onComplete()事件给前台，而当前message可能仍然执行着未完成的操作（如网络更新），如果
			 * 通过发送message-->excuteNextTask()的方式来处理，则由于当前message未完成，而
			 * 不能立即处理下一条message，从而导致onComplete()无法立即执行。*/
			finish(true, Task.ERROR_NO, Task.ERROR_NO, null);
		}
	}

	private void executeNextTask(Task task) {
		task.mTaskQueue = this;
		task.taskIndex = mCurrentTaskIndex;
		task.taskSize = mTaskSize;
		task.start();
	}

	private void success(int taskId, String message) {	//不能作为下一条message来处理，而是在当前Task所在的message处理。
		if(mCurrentTaskIndex < mTaskSize) mCurrentTaskIndex++;
		increaseProgress(taskId, 0, message);
		nextTask();
	}

	private void failure(int taskId, boolean interrupt, int errorType, int errorCode, String message) {
		if(mCurrentTaskIndex < mTaskSize) mCurrentTaskIndex++;
		increaseProgress(taskId, 0, message, errorType, errorCode);
		if(!interrupt) {
			nextTask();
		}else {
			finish(false, errorType, errorCode, message);
		}
	}

	private void increaseProgress(int taskId, float progress, String message) {
		increaseProgress(taskId, progress, message, Task.ERROR_NO, Task.ERROR_NO);
	}

	private void increaseProgress(int taskId, float progress, String message, int errorType, int errorCode) {
		slowMode();
		if(mTaskListener != null) mTaskListener.onProgress(taskId,
				//最多保留两位小数，如：30.25%
				Float.parseFloat(NumberUtils.floatFormat((mCurrentTaskIndex+progress)*100/mTaskSize, 3, 2, 0)),
				message, errorType, errorCode);
	}

	private void finish(boolean success, int errorType, int errorCode, String message) {
		boolean hasBgTask = !mIsBgTask && mBgTasks.size() > 0;
		if(mTaskListener != null) {
			if(success) {
				if(mIsBgTask) {
					mTaskListener.onUpdate();
				}else {
					mTaskListener.onComplete(hasBgTask);
				}
			}else {
				if(!mIsBgTask) mTaskListener.onFailure(errorType, errorCode, message);
			}
		}
		//只有任务成功，才继续执行衍生的后台任务
		if(success && hasBgTask) {
			//再次开始执行任务
			executeBgTasks();
		}else {
			//这里不将mStarted值为false，而在obtain()时才置为false。
			mTaskThread.recycle(this);
		}
	}

	/**添加一个背景任务，会在当前任务队列中的所有任务都执行完毕后执行**/
	private void queueBgTask(Task task) {
		mBgTasks.offer(task);
	}

	/**是否减慢运行速度。由于Task可能执行较快，看不到各个Task的细节，这里可以设置在两个Task之间休眠一定时间。
	 * @param slow				是否减慢速度
	 * @param sleepTimeMillis	间隔最大毫秒数(实际的间隔时间将在0到该数值之间随机取数)。注：只有在slow为true时起作用
	 **/
	public void setSlowMode(boolean slow, int sleepTimeMillis) {
		mSlow = slow;
		mSleepTimeMillis = sleepTimeMillis;
	}

	private void slowMode() {
		if(mSlow && mSleepTimeMillis > 0) {
			try {
				Thread.sleep(getSleepTime());
			} catch (InterruptedException e) {}
		}
	}

	private int getSleepTime() {
		if(mSlow) {
			float f = mSleepTimeMillis / 3.0f;
			return (int)((Math.random() * f * 2) + f + 1);
		}else {
			return 0;
		}
	}

	public void setTaskListener(TaskListener l) {
		mTaskListener = l;
	}

	@Override
	protected void finalize() throws Throwable {
		if(LOG) Log.i(LOG_TAG, "垃圾回收 TaskQueue ~~~~~~~~~~~ 相关线程 " + (mTaskThread != null ? mTaskThread.getName() + " 还未回收" : "mTaskThread 已回收"));
		super.finalize();
	}

	//*********************************************************************************//

	private static final AtomicInteger taskThreadCount = new AtomicInteger(0);
	private static final Queue<SoftReference<TaskQueue>> freeExecutorPool = new ConcurrentLinkedQueue<SoftReference<TaskQueue>>();
	private static final Queue<TaskBuilder> scheduledTQs = new ConcurrentLinkedQueue<TaskBuilder>();
	private static final TaskThread scheduler = new TaskThread();

	/**本方法的执行都是排着队的**/
	private static void executeScheduledTasks() {
		while(taskThreadCount.getAndIncrement() < maxTaskThreadSize) {
			if(LOG) Log.i(LOG_TAG, "执行计划任务--------count: "+(taskThreadCount.get()-1)+", waiting: "+scheduledTQs.size());
			TaskBuilder builder;
			TaskQueue taskQueue;
			synchronized (TaskQueue.class) {
				builder = scheduledTQs.poll();
				if (builder == null) break;
				taskQueue = obtain();
			}
			taskQueue.mTasks.addAll(builder.tasks);
			taskQueue.mTaskListener = builder.taskListener;
			taskQueue.executeTasks();
			builder.tasks = null;
			builder.taskListener = null;
		}
		taskThreadCount.decrementAndGet();	//注意跟 while 和 break 的巧妙配合
	}

	private static void recycle(TaskQueue taskQueue) {
		taskQueue.mTasks.clear();
		taskQueue.mBgTasks.clear();
		taskQueue.mCurrentTaskIndex = 0;
		taskQueue.mTaskSize = 0;
		taskQueue.mIsBgTask = false;
		taskQueue.mTaskListener = null;

		taskQueue.mSleepTimeMillis = 0;
		taskQueue.mSlow = false;

		synchronized (TaskQueue.class) {
			if (scheduledTQs.size() > freeExecutorPool.size() || freeExecutorPool.size() < maxFreePoolSize) {
				freeExecutorPool.offer(new SoftReference<TaskQueue>(taskQueue));
			} else {
				taskQueue.mTaskThread.quit();
				taskQueue.mTaskThread = null;
			}
		}
		taskThreadCount.decrementAndGet();
		scheduler.executeSchedule();
	}

	public static class TaskBuilder {
		private Queue<Task> tasks = new LinkedList<Task>();
		private boolean queued = false;
		private volatile TaskListener taskListener;

		public synchronized final void queueTask(Task task) {
			if(queued)  throw new AndroidRuntimeException("任务正在执行，不可再插入新任务");
			tasks.offer(task);
		}

		public void setTaskListener(TaskListener l) {
			taskListener = l;
		}

		public final void scheduleTasks() {
			synchronized(this) {
				if(queued) return;
				queued = true;
			}
			//添加到队列
			scheduledTQs.offer(this);
			scheduler.executeSchedule();
		}

		public final boolean undo() {
			boolean b = false;
			synchronized(this) {
				b = scheduledTQs.remove(this);
				if(b) queued = false;
			}
			return b;
		}
	}

	private static class TaskThread extends HandlerThread {
		private static final AtomicInteger count = new AtomicInteger(0);
		private static final int NEXT		= 0;
		private static final int EXECUTE	= 1;
		private static final int RECYCLE	= 2;

		private volatile boolean prepared = false, preparing = false;
		private Handler handler;
		private final boolean scheduler;
		private final TaskQueue taskQueue;

		public TaskThread() {
			super("SchedulerThread_" + count.getAndIncrement());
			scheduler = true;
			taskQueue = null;
		}

		public synchronized void executeSchedule() {
			if(prepared) {
				handler.sendEmptyMessage(EXECUTE);
			}else if(!preparing) {
				preparing = true;
				start();
			}
		}

		public TaskThread(TaskQueue tq) {
			super("TaskQueueThread_" + count.getAndIncrement());
			scheduler = false;
			taskQueue = tq;
		}

		public void nextTask(Task task, long delay) {
			handler.sendMessageDelayed(handler.obtainMessage(NEXT, task), delay);
		}

		public void recycle(TaskQueue taskQueue) {
			handler.sendMessage(handler.obtainMessage(RECYCLE, taskQueue));
		}

		@SuppressLint("HandlerLeak")
		protected void onLooperPrepared() {
			if(LOG) Log.i(LOG_TAG, "启动线程:"+getName());

			Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
			handler = new Handler() {
				public void handleMessage(Message msg) {
					switch(msg.what) {
					case NEXT:
						taskQueue.executeNextTask((Task)msg.obj);
						break;
					case RECYCLE:
						if(!scheduler && TaskQueue.scheduler.prepared) {
							TaskQueue.scheduler.recycle((TaskQueue)msg.obj);
						}else {
							TaskQueue.recycle((TaskQueue)msg.obj);
						}
						break;
					case EXECUTE:
						executeScheduledTasks();
						break;
					}
				}
			};
			prepared = true;
			if(scheduler) {
				executeSchedule();
			}else {
				taskQueue.onExecutorPrepared();
			}
		}

		@Override
		protected void finalize() throws Throwable {
			if(LOG) Log.i(LOG_TAG, "垃圾回收线程:"+getName());
			super.finalize();
		}
	}

	//*********************************************************************************//

	public interface TaskListener {
		void onStart();
		/**
		 * 为何要将errorType和errorCode都放在{@link #onProgress()}而不是{@link #onFailure()}中，是为了cover下面这个case:
		 * 若某Task设置为可忽略，则在任务失败后，会继续执行下一个任务，为了把错误信息传出去，so...
		 * @param taskId	标识task的id
		 * @param progress	取值范围是0~100
		 * @param message	进度信息或错误信息
		 * @param errorType	错误类型，由各个Task实现类自定义
		 * @param errorCode	某错误类型下的错误代码，自定义
		 **/
		void onProgress(int taskId, float progress, String message, int errorType, int errorCode);
		void onComplete(boolean hasBgTask);
		void onFailure(int errorType, int errorCode, String message);
		/**只有在onComplete()调用过之后，才有可能调用onUpdate()（后台任务成功完成之后）**/
		void onUpdate();
	}

	//*********************************************************************************//

	public static abstract class Task {
		protected static final String LOG_TAG = "Task";
		protected static final boolean LOG = Debug.LOG;

		public static final int ERROR_NO			= -1;
		public static final int ERROR_UNKNOWN		= -2;
		public static final int STATUS_WAITING		= -3;

		protected final int mId;

		private TaskQueue mTaskQueue;
		private volatile int taskIndex = 0;
		private volatile int taskSize = 0;

		private int errorType = ERROR_UNKNOWN;
		private int errorCode = ERROR_UNKNOWN;

		private int currentProgress = 0;
		private int maxProgress = 0;
		private float currentPersent = 0;

		private boolean finished = false;
		private boolean started = false;
		private boolean again = false;

		private String mProgressMsg;
		private String mSuccessMsg;
		private String mFailureMsg;

		public Task(int id) {
			mId = id;
		}

		/* 本方法不设为同步，否则当Task线程sleep()的时候，Task线程持有对象锁，
		 * increaseProcess()方法无法获得对象锁，无法执行。wait()方法除外，
		 * wait()执行完毕，移除对象锁，被吵醒之后才可能再次获得对象锁。
		 */
		private void start() {
			if(started || finished) throw new AndroidRuntimeException("任务已经执行过或者还没有完成");
			started = true;
			if(mTaskQueue != null) mTaskQueue.increaseProgress(mId, 0, mProgressMsg);
			run();
		}

		private void reset() {
			started = false;
			finished = false;
			currentProgress = 0;
			maxProgress = 0;
			currentPersent = 0;

			again = true;
			/*如果是构造方法中设置，则这里会永久清除，如果是运行时动态设置，则这里不用清除也无影响。so。。。
			mProgressMsg = null;
			mSuccessMsg = null;
			mFailureMsg = null;*/
		}

		public int getTaskIndex() {
			return taskIndex;
		}

		public int getTaskSize() {
			return taskSize;
		}

		public boolean isLastTask() {
			return taskIndex == taskSize-1;
		}

		public boolean isExcuteAgain() {
			return again;
		}

		/**添加一个背景任务。会在当前任务队列中的所有任务都执行完毕后执行。可以是当前task本身**/
		protected final void queueBgTask(Task task) {
			mTaskQueue.queueBgTask(task);
		}

		/**设置给外部显示的消息**/
		protected final synchronized void setMessage(String progress, String success, String failure) {
			mProgressMsg = progress!=null ? progress : mProgressMsg;
			mSuccessMsg = success!=null ? success : mSuccessMsg;
			mFailureMsg = failure!=null ? failure : mFailureMsg;

			if(started && !finished && progress!=null) {
				if(mTaskQueue != null) mTaskQueue.increaseProgress(mId, currentPersent, mProgressMsg);
			}
		}

		protected final synchronized void setErrorCode(int errorType, int errorCode) {
			this.errorType = errorType;
			this.errorCode = errorCode;
		}

		protected final synchronized void increaseProgress() {
			if(finished) {
				//由于无法控制多线程的中止，这里就不抛异常了
				/*throw new RuntimeException("本Task已经执行完毕，" +
					"不能再调用increaseProcess()，请检查线程是否执行完毕!~");*/
				return;
			}
			//让mProgressMsg消息能够发出去
			if(maxProgress==0) {
				currentPersent = 0;
			}else if(currentProgress>=maxProgress) {
				currentPersent = 1;
			}else {
				currentPersent = (float)++currentProgress/maxProgress;
			}
			if(mTaskQueue != null) mTaskQueue.increaseProgress(mId, currentPersent, mProgressMsg);
		}

		protected final synchronized void increaseProgressCount() {
			maxProgress++;
		}

		protected final synchronized void setProgressCount(int count) {
			currentProgress = 0;
			maxProgress = count;
		}

		protected final synchronized void success() {
			if(finished) throw new AndroidRuntimeException("Task.success()或者failure()只能调用其中一个一次");
			finished = true;
			if(mTaskQueue != null) mTaskQueue.success(mId, mSuccessMsg);
		}

		/**
		 * Task执行失败时调用本方法
		 * @param interrupt 是否中断执行任务队列
		 */
		protected final synchronized void failure(boolean interrupt) {
			if(finished) throw new AndroidRuntimeException("Task.success()或者failure()只能调用其中一个一次");
			finished = true;
			if(mTaskQueue != null) mTaskQueue.failure(mId, interrupt, errorType, errorCode, mFailureMsg);
		}

		/**注意应该在本方法的实现里面显示调用{@link #success()}或者{@link #failure(boolean, int)}
		 * 以使任务队列能够继续，并且在"执行路线中"能且只能调用任一方法一次，如果实现方法里面启动了多线程，
		 * 要确保多个线程总共只会对这两个方法的任意一个调用一次。**/
		protected abstract void run();
	}
}
